/*
 * OsgProject.h
 *
 *      Author: Boboss
 */

#ifndef OSGPROJECT_H_
#define OSGPROJECT_H_
#include <GL/glew.h>
#include "CShader.h"
#include "CFrameBufferObject.h"

#include <vrj/vrjConfig.h>

#include <iostream>
#include <iomanip>
#include <math.h>
#include <cstdlib>

#include <vrj/Draw/OGL/GlApp.h>
#include <gadget/Type/Position/PositionUnitConversion.h>

#include <gadget/Type/PositionInterface.h>
#include <gadget/Type/AnalogInterface.h>
#include <gadget/Type/DigitalInterface.h>

//OSG  includes
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Quat>
#include <osg/Matrix>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Transform>
#include <osg/Material>
#include <osg/NodeCallback>
#include <osg/Depth>
#include <osg/CullFace>
#include <osg/TexMat>
#include <osg/TexGen>
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
#include <osg/TextureCubeMap>
#include <osg/VertexProgram>
#include <osg/LineSegment>

#include <osgDB/Registry>
#include <osgDB/ReadFile>

#include <osgUtil/SmoothingVisitor>
#include <osgUtil/Optimizer>

#include <osg/MatrixTransform>
#include <osg/StateSet>
#include <osg/Math>
#include <osg/Shape>

#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>

#include <osgUtil/SceneView>
#include <osgUtil/IntersectVisitor>

#include <vrj/Draw/OSG/OsgApp.h>

#include <CSnowGenerator.h>
#include <CDeviceManager.h>
#include <CLogon.h>
#include <CMapNode.h>
#include <CJoueur.h>
#include <CSkyBox.h>
#include "CBombManager.h"
#include <Chud.h>
#include <CArm.h>

using namespace vrj;

#define MODEL_TREE 0
#define MODEL_ROCK 1
#define MODEL_CHALET 2
class OsgProject : public vrj::OsgApp{
private :
	CDeviceManager* _deviceManager;

	//OsgNavigator  mNavigator;       /** Navigation class */

	// Lumiere
	osg::LightSource * _lightSource1;

	// Variable nécessaire pour la synchronisation
	vpr::Interval last_frame; // Le temps de la derniere frame

	//Noeud principal
	osg::Group* mRootNode;
	osg::Node* map;
	osg::Group* _bombe;


	//Liste de noeuds utile pour la gestion des objets grabbés
	std::list<osg::PositionAttitudeTransform*> listNode;
	//Attribut servant de cache temporaire (utile pour la gestion des objets grabés)
	osg::PositionAttitudeTransform* temp;

	//Angle de rotation des objets
	float theta;

	// Classe représentant des noeud
	CSnowGenerator * _snowGenerator;
	CLogon * _login; // Le noeud de login
	CLogon * _logout; // Le noeud de logout
	CMapNode * _map; // La map et les objet dessus
	CJoueur * _joueur; // Le joueur
	CSkyBox * _skybox; // la skybox
	CBombManager * _bombManager;
	Chud * _hud; //le hud
	CArm * _arm; //le bras

	// Objet pour les fbo et shaders
	CFrameBufferObject _fboScreen;
	CFrameBufferObject _fboBloom1;
	CFrameBufferObject _fboBloom2;
	CFrameBufferObject _fboBloom3;
	CFrameBufferObject _fboBloom4;
	CFrameBufferObject _fboBloomBlur1;
	CFrameBufferObject _fboBloomBlur2;
	CFrameBufferObject _fboBloomBlur3;
	CFrameBufferObject _fboBloomBlur4;
	CShader _shaderBoxBlur;
	CShader _shaderAddEffect;
	CShader _shaderLuminenceTreshold;
	CShader _shaderCopy;
	CShader _shaderFlash;
	CShader _shaderDistortion;

	// Variable pour indiquer si on utilise le bloom
	bool 	_useBloom;
	int		_typeBloom;
	// Variable pour gerer le fash
	int 	_flashStep;
	float 	_flashValue;
	float 	_flashTimeMax;
	float 	_flashTime;
	float 	_deltaTime;

	// Différents step du jeu (login=0,fame=1,logout=2)
	int _gameStep;
	// Variables pour gerer le temps d'affichage du step  0
	float _step0TimeMax;
	float _stepTimeElapsed;

	// Variable nécessaire au step de jeu 1
	float _timeDistort; // Variable qui indique le temps qu'il faut pour qu'une distortion arrive
	float _bombeTimer;
	float _bombeTimeMax;
	bool _bombeExplose;
	bool _bombPoped;
	bool _popBomb;
	bool _bombDiffused;
	bool _bombGrabbed;
	int _bombNum;
	int _bombDiffuse;

	// Variable pour gerer la distortion
	float	_distortionTimeMax;
	float 	_distortionTime;
	float	_distortionValue;
	int 	_distortionStep;

	// Variables pour les boutons
	bool _button1StateUsed;
	bool _button2StateUsed;
	bool _button3StateUsed;

	// Taille de l'affichage
	int _screenWidth;
	int _screenHeigth;

	// Fonctions pour les effets de shaders
	void fakeRenderForShader();
	void generateBloomEffect();
	void generateFlashEffect(float delta);
	void generateDistortionEffect(float delta);

	// Fonction de gestion des step du jeu
	void stepManagment(float delta);

	void addBomb(osg::Node* b){ _bombe->addChild(b); }
	void removeBomb(){ _bombe->removeChildren(0,_bombe->getNumChildren()); }

public:
	   OsgProject(vrj::Kernel* kern, int& argc, char** argv);

#if CAVE == 0
	   void draw();
#endif
	   virtual ~OsgProject()
	   {
	      /* Do nothing. */ ;
	   }

	virtual void initScene();

	   virtual osg::Group* getScene()
	   {
	      return mRootNode;
	   }

	   virtual void configSceneView(osgUtil::SceneView* newSceneViewer)
	   {
			vrj::OsgApp::configSceneView(newSceneViewer);

			// On regarde si les fbo on été crée, sinon on le crée
			osgUtil::SceneView* sv;
			sv = newSceneViewer;

			_screenWidth = (int)(sv->getViewport()->width());
			_screenHeigth = (int)(sv->getViewport()->height());



#if CAVE == 0
			_hud = new Chud(_screenWidth, _screenHeigth);
			glewInit();

			if(_fboScreen.isCreated()!=true)
			{
				if(_fboScreen.CreateFBO((GLuint)(sv->getViewport()->width()),(GLuint)(sv->getViewport()->height()))!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboScreen" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboScreen à bien été crée" << std::endl;
			}

			if(_fboBloom1.isCreated()!=true)
			{
				if(_fboBloom1.CreateFBO((GLuint)(sv->getViewport()->width()),(GLuint)(sv->getViewport()->height()))!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloom1" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloom1 à bien été crée" << std::endl;
			}

			if(_fboBloom2.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloom2.CreateFBO((GLuint)(sv->getViewport()->width())/2,(GLuint)(sv->getViewport()->height())/2)!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloom2" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloom2 à bien été crée" << std::endl;
			}
			if(_fboBloom3.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloom3.CreateFBO((GLuint)(sv->getViewport()->width())/4,(GLuint)(sv->getViewport()->height())/4)!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloom3" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloom3 à bien été crée" << std::endl;
			}

			if(_fboBloom4.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloom4.CreateFBO((GLuint)(sv->getViewport()->width())/8,(GLuint)(sv->getViewport()->height())/8)!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloom4" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloom4 à bien été crée" << std::endl;
			}


			if(_fboBloomBlur1.isCreated()!=true)
			{
				if(_fboBloomBlur1.CreateFBO(_fboBloom1.getWidth(),_fboBloom1.getWidth())!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloomBlur1" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloomBlur1 à bien été crée" << std::endl;
			}

			if(_fboBloomBlur2.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloomBlur2.CreateFBO(_fboBloom2.getWidth(),_fboBloom2.getWidth())!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloomBlur2" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloomBlur2 à bien été crée" << std::endl;
			}
			if(_fboBloomBlur3.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloomBlur3.CreateFBO(_fboBloom3.getWidth(),_fboBloom3.getWidth())!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloomBlur3" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloomBlur3 à bien été crée" << std::endl;
			}
			if(_fboBloomBlur4.isCreated()!=true && _typeBloom==0)
			{
				if(_fboBloomBlur4.CreateFBO(_fboBloom4.getWidth(),_fboBloom4.getWidth())!=1)
				{
					std::cout << "Probleme lors de la création du frame buffer _fboBloomBlur4" << std::endl;
				}
				else
					std::cout << "Le frame buffer _fboBloomBlur4 à bien été crée" << std::endl;
			}


			// On regarde si les shaders on été loadé, sinon on les load
			if(_shaderBoxBlur.isLoaded()!= true)
			{
				// On charge le shader de flou
				_shaderBoxBlur.Load("boxBlur");
			}
			if(_shaderAddEffect.isLoaded()!= true)
			{
				// On charge le shader charger de binder tout les effets
				_shaderAddEffect.Load("addEffect");
			}
			if(_shaderLuminenceTreshold.isLoaded()!= true)
			{
				// On charge le shader extraction de luminence
				_shaderLuminenceTreshold.Load("thresoldBrightness");
			}
			if(_shaderCopy.isLoaded()!= true)
			{
				// On charge le shader charger de copy d'un fbo
				_shaderCopy.Load("copy");
			}
			if(_shaderFlash.isLoaded()!= true)
			{
				// On charge le shader charger de copy d'un fbo
				_shaderFlash.Load("flash");
			}
			if(_shaderDistortion.isLoaded()!= true)
			{
				// On charge le shader charger de copy d'un fbo
				_shaderDistortion.Load("distortion");
			}
#endif
			newSceneViewer->getLight()->setAmbient(osg::Vec4(0.3f,0.3f,0.3f,1.0f));
			newSceneViewer->getLight()->setDiffuse(osg::Vec4(0.9f,0.9f,0.9f,1.0f));
			newSceneViewer->getLight()->setSpecular(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
	   }

	   void bufferPreDraw();

	      //: Function called after tracker update but before start of drawing<BR><BR>
	      //
	      // called once before every frame.
	      virtual void preFrame();

	      // Function called after ApplicationData syncronization but before draw()
	      virtual void latePreFrame();

	      //: Function called after drawing has been triggered but BEFORE it completes<BR><BR>
	      //
	      // called once during each frame
	      virtual void intraFrame()
	      {
	         // Put your intra frame computations here.
	      }

	      //: Function called before updating trackers but after the frame is drawn<BR><BR>
	      //
	      // called once after every frame
	      virtual void postFrame()
	      {
	         // Put your post frame computations here.
	      }

	      /**
	       * Returns the scale factor for this application.  For this sample, we
	       * are handling everything in feet.
	       */
	      virtual float getDrawScaleFactor()
	      {
	         return gadget::PositionUnitConversion::ConvertToFeet;
	      }
};

#endif /* OSGPROJECT_H_ */
